#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include <time.h>
#include <cstdlib>
#include <unistd.h>

namespace ns_blockqueue
{
    const int default_cap = 5;
    template <class T>
    class BlockQueue
    {
    private:
        std::queue<T> _bq;
        int _cap;             // 容量上限
        pthread_mutex_t _mtx; // 锁
        pthread_cond_t _is_full;
        pthread_cond_t _is_empty;

        bool IsFull()
        {
            return _bq.size() == _cap;
        }

        bool IsEmpty()
        {
            return _bq.size() == 0;
        }

        void LockQueue()
        {
            pthread_mutex_lock(&_mtx);
        }

        void UnlockQueue()
        {
            pthread_mutex_unlock(&_mtx);
        }

        void ProducterWait()
        {
            pthread_cond_wait(&_is_empty, &_mtx);
        }
        void ConsumerWait()
        {
            pthread_cond_wait(&_is_full, &_mtx);
        }

        void WakeUpProducter()
        {
            pthread_cond_signal(&_is_empty);
        }
        void WakeUpConsumer()
        {
            pthread_cond_signal(&_is_full);
        }

    public:
        BlockQueue(int cap = default_cap) : _cap(cap)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_is_empty, nullptr);
            pthread_cond_init(&_is_full, nullptr);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_is_empty);
            pthread_cond_destroy(&_is_full);
        }

    public:
        void Push(const T &in)
        {
            LockQueue();
            while (IsFull())
            {
                ProducterWait();
            }
            _bq.push(in);
            WakeUpConsumer();
            UnlockQueue();
        }
        void Pop(T *out)
        {
            LockQueue();
            while (IsEmpty())
            {
                ConsumerWait();
            }
            *out = _bq.front();
            _bq.pop();
            WakeUpProducter();
            UnlockQueue();
        }
    };

}